#include "device.h"
#include "component.h"
#include "adpcm.h"
#include "audio_data.h"
#include "audio.h"
#ifdef FUNC_AUDIO_DIY
#include "diy_audio.h"
#endif

#define AUDIO_LOG(format, ...)          __OSAL_LOG("[audio.c] " C_LIGHT_GREEN format C_NONE, ##__VA_ARGS__)

/* 默认语音组件的昵称 */
#ifndef KOS_PARAM_AUDIO_ALIAS
#define KOS_PARAM_AUDIO_ALIAS  AUDIO_ALIAS_FRONT
#endif

/* NV AUDIO组件初始化标识*/
#define AUDIO_NV_INIT_FLAG				0xFA

/* 扫描语音设备状态事件 */
#define EVENT_SCAN_DEV_STA				( 0X00000001 )
#define EVENT_FLASHERR_PUBLISH          ( 0X00000002 )
#define EVENT_MUTE_CTRL                 ( 0X00000004 )
#define EVENT_MD5_PUBLISH               ( 0X00000008 )

/* 定义缓存大小 */
#define AUDIO_RING_BUFFER_LEN  			( 40 )
#define AUDIO_RING_BUFFER_SIZE 			( sizeof(uint16_t) )
	


// 正在播放以下声音时，禁止播放其它语音
const uint16_t prohibitPlayTab[] = 
{
    YI_TUI_CHU_GUAN_LI_MO_SHI,          //已退出管理模式
};

// 在设置为静音模式下,下面的语音仍然可以正常播放
const uint16_t muteInvalidTab[] = 
{
	BAO_JING_YIN_DA_DA_DA,							//报警音嗒嗒嗒
	JING_YIN_MO_SHI,								//静音模式
	YI_JIN_RU_GUAN_LI_MO_SHI,						//已进入管理模式
	DIAN_LIANG_DI_QING_GENG_HUAN_DIAN_CHI,			//电量低，请更换电池
	MEN_WEI_SHANG_SUO,								//门未上锁
	XI_TONG_YI_SUO_DING_QING_SHAO_HOU_ZAI_SHI,		//系统已锁定，请稍后再试
    QING_ZAI_MEN_WAI_AN_JING_HAO_JIAN_QUE_REN_CAO_ZUO, //请在门外按#号键确认操作
    DIAN_CHI_DIAN_LIANG_DI_QING_JI_SHI_GENG_HUAN_DIAN_CHI_HUO_CHONG_DIAN,//电池电量低，请及时更换电池或充电
};


/* 创建环形缓存，保存待播放的语音索引号 */
static RingBufferHandle_t rbAudioHandle = NULL;

/* 语音信息 */
static SoundInfo_stu_t soundInfo;

/* 语音nv信息 */
__EFRAM static AuidoNv_stu_t nvAudioInfo;

/* 停止标志 */
static uint8_t audioStopflag = 1;

/* 当前播放的语音文件，剩余的（还未播放的）数据 */
static int32_t audioRemainderDataSize=0;		

/* 缓存大小 */
static uint16_t bufferSize = 0;

/* 记录当前正在播放的语音序号 */
static uint16_t active_audio_num = 0;

/* 语音状态（0：空闲   1：忙） */
static FlagStatus audio_status = RESET;

/* 禁止播放标志（正在播放不可打断的语音该标志为1） */
static FlagStatus prohibit_play = RESET;

static uint8_t playFlag = 0;    //播放文件的类型标志位 0：adpcm  1:pcm

#ifdef AUDIO_I2S_DAC
static uint32_t audio_start_timestamp;
#endif

static uint8_t Disable_Mute_Flag = 0;      /* 禁止静音标志 */


/**
  * @brief  设置语言
  * @note   
  */
static ErrorStatus Audio_SetLanguageApi(uint8_t lan)
{
    uint8_t tmp = lan;
	nvAudioInfo.language = lan;
    OSAL_NvWrite(OSAL_OFFSET(AuidoNv_stu_t, language), &tmp, sizeof(tmp));
	AUDIO_LOG("SetLanguage = %d\r\n", lan);
    return SUCCESS;
}

/**
  * @brief  设置音量
  * @note   
  */
static ErrorStatus Audio_SetVolumeApi(uint8_t vol)
{
	if(vol <= 100 )
	{
		int32_t ret = Device_Write(vAUDIO_0, NULL, 0, vol);
		if(ret == -1)
		{
			return ERROR;
		}
		nvAudioInfo.volume = vol;
		OSAL_NvWrite(OSAL_OFFSET(AuidoNv_stu_t, volume), &nvAudioInfo.volume, sizeof(nvAudioInfo.volume));
		return SUCCESS;
	}
	else
	{
		return ERROR;
	}
}

/**
  * @brief  设置静音模式
  * @note   
  */
static ErrorStatus Audio_SetMuteApi(uint8_t flag)
{
	nvAudioInfo.mute = flag;
	OSAL_NvWrite(OSAL_OFFSET(AuidoNv_stu_t, mute), &nvAudioInfo.mute, sizeof(nvAudioInfo.mute));
	return SUCCESS;
}


/**
  * @brief  Audio组件NV初始化
  * @note   
  */
static void Audio_NvInit(void)
{
    __EFRAM static FlagStatus power_on = RESET;

    if (power_on != SET)
    {
        power_on = SET;

        //< 读出数据
        memset(&nvAudioInfo, 0, sizeof(nvAudioInfo));
        OSAL_NvRead(0, &nvAudioInfo, sizeof(AuidoNv_stu_t));

        //< NV RESET
        if (nvAudioInfo.nvInitFlag != AUDIO_NV_INIT_FLAG)
        {
            AUDIO_LOG("Audio Nv Reset\r\n");
            nvAudioInfo.nvInitFlag = AUDIO_NV_INIT_FLAG;
            nvAudioInfo.language = LANGUAGE_CHINESE;
            nvAudioInfo.volume = 100;
            nvAudioInfo.mute= RESET;
        }

        //< 读语音版本
        Device_Read(vFLASH_0, nvAudioInfo.version, sizeof(nvAudioInfo.version), 0);
        OSAL_NvWrite(0, &nvAudioInfo, sizeof(nvAudioInfo));

        OSAL_EventSingleCreate(COMP_AUDIO, EVENT_FLASHERR_PUBLISH, 100, EVT_PRIORITY_MEDIUM);

#ifdef FUNC_AUDIO_DIY
        // diy reset
        Audio_DIYInit();
        // 上报md5事件
        OSAL_EventSingleCreate(COMP_AUDIO, EVENT_MD5_PUBLISH, 0, EVT_PRIORITY_MEDIUM); //I2S使能后，延后一段时间再开功放，解决刚启动那会的噗呲声

        // test
        // DIYAudio_stu_t msg={
        //     .SN = 203,
        //     .total_len = 20,
        //     .pack_len = 20,
        // };
        // memset(&msg.data, 0xAA, msg.pack_len);
        // Audio_WriteDIY(&msg);

        // DIYAudio_stu_t msg1={
        //     .SN = 203,
        //     .total_len = 987,
        //     .pack_len = 987,
        // };
        // memset(&msg1.data, 0xBB, msg.pack_len);
        // Audio_WriteDIY(&msg1);
#endif
    }
    
    //< 音量设置到底层驱动
    Device_Write(vAUDIO_0, NULL, 0, nvAudioInfo.volume);
}

/**
  * @brief  当前语音是否在prohibitPlayTab里面
  * @note   
  * @param  audioIndex： 语音索引
  * @return SET:在prohibitPlayTab里面 RESET：不在prohibitPlayTab里面
  */
static FlagStatus Audio_IsProhibit(uint16_t audioIndex)
{
    uint8_t j;
    uint8_t tabLen = OSAL_LENGTH(prohibitPlayTab);

    for(j = 0 ; j < tabLen; j++)
    {
        if(audioIndex%AUDIO_LANGUAGE_ADDR_OFFSET ==  prohibitPlayTab[j])
        {
            return SET;
        }
    }
    return RESET;
}

/**
  * @brief  当前语音是否在静音无效列表里面
  * @note   
  * @param  audioIndex： 语音索引
  * @return SET:在prohibitPlayTab里面 RESET：不在prohibitPlayTab里面
  */
static FlagStatus Audio_IsMuteInvalidTab(uint16_t audioIndex)
{
	uint8_t  j;
    uint8_t tabLen = OSAL_LENGTH(muteInvalidTab);
    for(j = 0 ; j < tabLen; j++)
    {
        if(audioIndex%AUDIO_LANGUAGE_ADDR_OFFSET == muteInvalidTab[j])
        {
            return SET;
        }
    }
    return RESET;
}

/**
  * @brief  加载语音数据
  *
  * @note
  */
static ErrorStatus Audio_DataLoading(void)
{
    uint8_t *sAudioDataBuffer = NULL;
    int32_t dataSize = audioRemainderDataSize;

    sAudioDataBuffer = (uint8_t*)OSAL_Malloc(bufferSize);
    if (sAudioDataBuffer == NULL)
    {
        AUDIO_LOG("mem err:%d\r\n", bufferSize);
        return ERROR;
    }
    memset(sAudioDataBuffer, 0, bufferSize);
    

    if(playFlag == 0)   //adpcm
    {
        if (dataSize > (bufferSize / 4))
        {
            dataSize = bufferSize / 4;
        }
        Audio_ReadAdpcmAndDecode(sAudioDataBuffer, soundInfo.addr + soundInfo.length - audioRemainderDataSize, dataSize);
    }
    else            //pcm
    {
        if (dataSize > bufferSize)
        {
            dataSize = bufferSize;
        }
        Device_Read(vFLASH_0, sAudioDataBuffer, dataSize, soundInfo.addr + soundInfo.length - audioRemainderDataSize);
    }

    //写AUDIO缓存
    Device_Write(vAUDIO_0, sAudioDataBuffer, bufferSize, 0);
    OSAL_Free(sAudioDataBuffer);

    /* 记录剩余数据大小 */
    audioRemainderDataSize -= dataSize;//未读取的数据大小
    return SUCCESS;
}

/**
  * @brief  播放下一条待播放的语音
  *
  * @note
  */
static ErrorStatus Audio_PlayNext(uint16_t sn)
{
#ifdef FUNC_AUDIO_DIY
    uint8_t diy_addr = Audio_FindSN(sn);
    AUDIO_LOG("diy_addr = 0x%x \r\n", diy_addr);
    if (diy_addr == 0xFF)
    {
        /* 读取音频文件信息 */
        Audio_GetInfo(sn, &soundInfo, &playFlag);
    }
    else
    {
        Audio_GetDIYInfo(diy_addr, &soundInfo);
        playFlag = 0;
    }
#else
    /* 读取音频文件信息 */
    Audio_GetInfo(sn, &soundInfo, &playFlag);
#endif // #ifdef FUNC_AUDIO_DIY

    AUDIO_LOG("soundInfo.addr = 0x%08lx \r\n", soundInfo.addr);
    AUDIO_LOG("soundInfo.length = %ld \r\n", soundInfo.length);
    audioRemainderDataSize = soundInfo.length;

    /* 加载音乐数据 */
    if (audioRemainderDataSize > 0)
    {
        ADPCM_Init();
    #ifndef AUDIO_I2S_DAC //I2S底层驱动一上电就在跑，只允许在中断里面加载数据
        if (Audio_DataLoading() != SUCCESS)
        {
            return ERROR;
        }

        if (audioRemainderDataSize > 0)
        {
            Audio_DataLoading();
            audioStopflag = 0;
        }
        else
        {
            audioStopflag = 1;
        }

        /* 使能音频设备 */
        Device_Enable(vAUDIO_0);
    #endif
        return SUCCESS;
    }
    return ERROR;
}

/**
  * @brief  语音设备中断处理
  *
  * @note   直接在中断里面加载语音数据，否则会断流
  */
static void Audio_ProcessISR(void)
{
    if (audioRemainderDataSize > 0)
    {
        /* 加载音乐数据 */
        Audio_DataLoading();
    }
    else
    {
        /* 当前语音数据已经加载完成，待下一次DMA传输结束就执行stop */
        if (audioStopflag >= 1)
        {
            Device_Write(vAUDIO_0, NULL, 0, UINT32_MAX);	/* 停止播放 */
            audio_status = RESET;
        }
        else
        {
            audioStopflag = 1;
        }
    }
}

static void DoorBell_Close_cb(TimerHandle_stu_t handle)
{
    // AUDIO_LOG("doorbell close\r\n");
    Device_Write(vPIN_C20,NULL,0,1);
}

/**
  * @brief  使能AUDIO
  * @note   
  * @param  sn： 播放的编号
  * @param  opt：1：立即播
  *              2：空闲才播
  *              其他值：排队播
  * @return 成功/失败
  */
static ErrorStatus Audio_PlayApi(uint16_t sn, uint8_t opt)
{
    if (sn == 0)
    {
        return ERROR;  //无效语音编号
    }
    
    if(sn == HUAN_YING_YIN)
    {
        AUDIO_LOG("doorbell ring\r\n");
        Device_Write(vPIN_C20,NULL,0,0);
        OSAL_TimerCreate(DoorBell_Close_cb, 1000, RESET);
    }

    if (prohibit_play == SET) 
    {
        // 正在播放prohibitPlayTab列表里面的条目，禁止播放其它语音
        return ERROR;
    }

    if ((audio_status != RESET) && (opt == 2))
    {
        // opt参数为2：当前语音正忙就不播
        return ERROR;
    }

    AUDIO_LOG("Audio_PlayApi\r\n");
    if (sn > AN_JIAN_YIN_DI && sn < AUDIO_LANGUAGE_ADDR_OFFSET)
    {
        sn += nvAudioInfo.language * AUDIO_LANGUAGE_ADDR_OFFSET;
    }
    AUDIO_LOG("sn = %d\r\n", sn);

    if (opt == 1 || sn == STOP_PLAY)
    {
        OSAL_RingBufferReset(rbAudioHandle);         // Reset Ringbuffer

    #ifndef AUDIO_I2S_DAC //I2S底层只允许在ISR函数里面WRITE
        Device_Write(vAUDIO_0, NULL, 0, UINT32_MAX); // 停止播放
    #endif
    
        audioRemainderDataSize = 0;
        audio_status = RESET;
    }

    if (sn != STOP_PLAY)
    {
        if (Disable_Mute_Flag != 0 || nvAudioInfo.mute == RESET || Audio_IsMuteInvalidTab(sn))
        {
            // 非静音状态、或者在静音无效表里面：则需要写入播放队列
            OSAL_RingBufferWrite(rbAudioHandle, &sn);
            OSAL_EventSingleCreate(COMP_AUDIO, EVENT_SCAN_DEV_STA, 0, EVT_PRIORITY_MEDIUM);
            OSAL_EventRepeatCreate(COMP_AUDIO, EVENT_SCAN_DEV_STA, 50, EVT_PRIORITY_MEDIUM);

            if (Audio_IsProhibit(sn) == SET) 
            {
                prohibit_play = SET;
            }
        }
    }
    return SUCCESS;
}

/**
  * @brief  将字符串转换成语音编号
  *         
  */
static uint8_t Audio_string2audio(uint8_t *str, uint16_t *audio)
{
	uint16_t num, cnt = 0;
	
	while (*str != '\0')
	{
		num = 0;
		if (*str == '0')
		{
			num = YU_YIN_0;
		}
		else if (*str >= '1' && *str <= '9')
		{
			num = YU_YIN_1 + (*str - '1');
		}
		else if (*str >= 'A' && *str <= 'Z')
		{
			num = YU_YIN_A + (*str - 'A');
		}
		else if (*str >= 'a' && *str <= 'z')
		{
			num = YU_YIN_A + (*str - 'a');
		}
		else if (*str == '-')
		{
			num = ZHI_WEN_YIN_DO;
		}
		str++;
		if (num != 0)
		{
			audio[cnt++] = num;
		}
	}
    audio[cnt++] = ZHI_WEN_YIN_DO;
    return cnt;
}

/**
  * @brief  播放字符串
  *         
  */
static ErrorStatus Audio_PlayStringApi(char *str)
{
	uint16_t audioBuffer[30], len;

    len = Audio_string2audio((uint8_t *)str, audioBuffer);
    for (int i = 0; i < len; i++)
    {
		Audio_PlayApi(audioBuffer[i], RESET);
    }
    return SUCCESS;
}

/**
  * @brief  扫描设备状态
  *
  * @note
  */
static void Audio_ScanDeviceStatus(void)
{
#ifdef AUDIO_I2S_DAC
    uint32_t time = OSAL_GetTickCount();
    if (OSAL_PastTime(time, audio_start_timestamp) < 500)
    {
        return; /* 刚唤醒的前几百毫秒不能播放，要等待I2S-DAC芯片就绪 */
    }
#endif

    if (audio_status == RESET) //当前语音播放结束 
    {
        /* 通知应用层当前这句播放结束 */
        if ((active_audio_num != 0))
        {
            AudioPublishMsg_t msg;
            msg.number = active_audio_num;
            OSAL_MessagePublish(&msg, sizeof(msg));

            if (Audio_IsProhibit(active_audio_num) == SET) 
            {
                prohibit_play = RESET;
            }
        }

        /* 播放下一句 */
        uint16_t number;
        if (OSAL_RingBufferRead(rbAudioHandle, &number) != ERROR)
        {
            if (Audio_PlayNext(number) != ERROR)
            {
                audio_status = SET;
                active_audio_num = number;
                OSAL_SetTaskStatus(TASK_STA_ACTIVE);
            }
        }
        else
        {
            OSAL_EventDelete(COMP_AUDIO, EVENT_SCAN_DEV_STA);//删除扫描事件
            active_audio_num = 0;
            OSAL_SetTaskStatus(TASK_STA_NORMAL);
        }
    }
}

/**
  * @brief  语音中断处理
  * @note   
  * @return 
  */
static void Audio_Int_Irq_Handler(VirtualHardware_enum_t dev, void *data, uint32_t len)
{
    Audio_ProcessISR();
}

static void Audio_Init(void)
{
    /* NV初始化 */
    Audio_NvInit();

    /* 配置语音组件的昵称 */
    OSAL_SetCompAlias(KOS_PARAM_AUDIO_ALIAS);

    /* 获取语音缓存SIZE */
    bufferSize = Device_GetDeviceCtrlBlock(vAUDIO)->devParam;

    /* 创建环形缓存、注册语音中断回调 */
    if (rbAudioHandle == NULL)
    {
        rbAudioHandle = OSAL_RingBufferCreate(AUDIO_RING_BUFFER_LEN, AUDIO_RING_BUFFER_SIZE);

        Device_RegisteredCB(vAUDIO_0, Audio_Int_Irq_Handler);
    }
}

static void Audio_ProcessMbox(uint8_t *msg)
{
    uint16_t sn;

    switch (msg[0])
    {
    case AUDIO_MBOX_PLAY:
        memcpy(&sn, &msg[2], 2);
        Audio_PlayApi(sn, (FlagStatus)msg[1]);
        AUDIO_LOG("free mem size: %d, %d\r\n", OSAL_GetMinimumEverFreeHeapSize(), OSAL_GetFreeHeapSize());
        break;

    case AUDIO_MBOX_PLAYSTR:
        Audio_PlayStringApi((char*)&msg[1]);
        break;

    case AUDIO_MBOX_SET_LAN:
        Audio_SetLanguageApi(msg[1]);
        break;

    case AUDIO_MBOX_SET_VOL:
        Audio_SetVolumeApi(msg[1]);
        break;

    case AUDIO_MBOX_SET_MUTE:
        Audio_SetMuteApi(msg[1]);
        break;
    case AUDIO_MBOX_NOT_MUTE:
        if (msg[1] != 0)
        {
            Disable_Mute_Flag |= msg[2];
        }
        else
        {
            Disable_Mute_Flag &= ~msg[2];    
        }
        break;
#ifdef FUNC_AUDIO_DIY
    case AUDIO_MBOX_TITLE_DIY: // 首包
        Audio_TitleDIY(&msg[1]);
        break;
    case AUDIO_MBOX_SEND_DIY: // 发数据
        Audio_WriteDIY(&msg[1]);
        break;
    case AUDIO_MBOX_DEL_DIY://删
        Audio_DeletDIY(&msg[1]);
        break;
#endif
    }
}

/**
  * @brief  语音任务函数
  *
  * @note
  *         
  * @param  event：当前任务的所有事件
  *
  * @return 返回未处理的事件
  */
static uint32_t Audio_Task(uint32_t event)
{
    /* 系统启动事件 */
    if (event & EVENT_SYS_START)
    {
        AUDIO_LOG("audio task start\r\n");
        Device_Enable(vFLASH_0);

    #ifndef AUDIO_CHIP_SC8002B                  // SC8002B功放芯片静音要先打开, 否则唤醒后第一条语句播放不全. 猜测是对0.47uF电容充电导致
        Device_Write(vPIN_C19, NULL, 0, 1);
    #else
        Device_Write(vPIN_C19, NULL, 0, 0);
    #endif
        Audio_Init();
        
    #ifdef AUDIO_I2S_DAC
        Device_Enable(vAUDIO_0);
        audio_start_timestamp = OSAL_GetTickCount();
        OSAL_EventSingleCreate(COMP_AUDIO, EVENT_MUTE_CTRL, 430, EVT_PRIORITY_MEDIUM); //I2S使能后，延后一段时间再开功放，解决刚启动那会的噗呲声
    #endif
        return ( event ^ EVENT_SYS_START );
    }

    /* 扫描设备状态事件 */
    if (event & EVENT_SCAN_DEV_STA)
    {
        Audio_ScanDeviceStatus();
        return ( event ^ EVENT_SCAN_DEV_STA );
    }

    if (event & EVENT_SYS_MBOX)
    {
        uint16_t size;
        while (OSAL_MboxLenght(&size) > 0)
        {
            uint8_t *mbox_buffer = (uint8_t *)OSAL_Malloc(size);
            if (mbox_buffer == NULL)
            {
                AUDIO_LOG("memory error\r\n");
                Device_EnterCritical();
                while (1);
            }
            OSAL_MboxAccept(mbox_buffer);
            Audio_ProcessMbox(mbox_buffer);
            OSAL_Free(mbox_buffer);
        }
        return ( event ^ EVENT_SYS_MBOX );
    }

    /* 系统休眠事件 */
    if (event & EVENT_SYS_SLEEP)
    {
        AUDIO_LOG("sleep\r\n");
    
    #ifdef AUDIO_I2S_DAC // 必须先控制静音，再Disable-I2S，防止产生噗呲声
        Device_Write(vPIN_C31, NULL, 0, 0);
    #endif

        Device_Disable(vAUDIO_0);
        Device_Disable(vFLASH_0);

    #ifdef AUDIO_CHIP_SC8002B
        Device_Write(vPIN_C19, NULL, 0, 1);          // SC8002B语音芯片这里静音使能,否则其掉电时,有余留一个掉电破音
    #else
        Device_Write(vPIN_C19, NULL, 0, 0);
    #endif
        return ( event ^ EVENT_SYS_SLEEP );
    }

    if (event & EVENT_FLASHERR_PUBLISH)
    {
        if (KOS_PARAM_AUDIO_ALIAS == AUDIO_ALIAS_FRONT)
        {
            uint8_t err = 1;
            if ((nvAudioInfo.version[0] >= '0' && nvAudioInfo.version[0] <= '9') || (nvAudioInfo.version[0] >= 'A' && nvAudioInfo.version[0] <= 'Z'))
            {
                err = 0;
            }
            OSAL_MessagePublishErrorCode(ERRCODE_TYPE_FLASH, err);
        }
        return ( event ^ EVENT_FLASHERR_PUBLISH );
    }

#ifdef AUDIO_I2S_DAC
    if (event & EVENT_MUTE_CTRL)
    {
        Device_Write(vPIN_C31, NULL, 0, 1); //功放静音控制脚：使能功放输出
        return (event ^ EVENT_MUTE_CTRL);
    }
#endif
#ifdef FUNC_AUDIO_DIY
    if (event & EVENT_MD5_PUBLISH)
    {
        Audio_PowerPublishMD5();// 每次上电上报MD5
        return (event ^ EVENT_MD5_PUBLISH);
    }
#endif
    return 0;
}
#ifdef FUNC_AUDIO_DIY
COMPONENT_TASK_EXPORT(COMP_AUDIO, Audio_Task, (sizeof(AuidoNv_stu_t) + sizeof(DIYAudioInfo_stu_t)));
#else
COMPONENT_TASK_EXPORT(COMP_AUDIO, Audio_Task, sizeof(AuidoNv_stu_t));
#endif
char import_kos_audio;